#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <syserr.h>
#include <device.h>
#include <utils.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/epoll.h>
#include "device.h"
#include "evtloop.h"
#include "dev/stream.h"

#define to_tty(stream_dev) container_of(stream_dev, struct tty_device, stream_dev)

struct tty_device
{
    struct stream_device    stream_dev;
    struct tty_cfg          cfg;
};

static int tty_open(struct stream_device *stream_dev)
{
    device_t *dev = to_dev(stream_dev);
    struct tty_device *tty = to_tty(stream_dev);

    int fd = tty->cfg.fd;

    ev__io_init(&dev->io, device_io, fd);
    ev__io_start(dev_loop(dev), &dev->io, EPOLLIN);
    dev_status(dev) = DEVICE_STATUS_CONNECTED;
    return fd;
}
static void tty_eof(struct stream_device *stream_dev)
{
    device_t *dev = to_dev(stream_dev);

    device_reset(dev);

    if (dev->usr_cb && dev->usr_cb->ev_cb)
        dev->usr_cb->ev_cb(dev, DE_DISCONN, NULL);
}
static int tty_ctrl(struct stream_device *stream_dev, u32 cmd, void *args)
{
    struct tty_device *tty = to_tty(stream_dev);

    switch (cmd)
    {
    case DEV_CTRL_CMD_GET_CFG:
        memcpy(args, &tty->cfg, sizeof(tty->cfg));
        break;
    case DEV_CTRL_CMD_SET_CFG:
        memcpy(&tty->cfg, args, sizeof(tty->cfg));
        break;
    }
    return 0;
}
static const struct stream_operations tty_ops =
{
    .open   = tty_open,
    .on_eof = tty_eof,
    .ctrl   = tty_ctrl,
};

static const struct tty_cfg tty_cfg_def =
{
    .fd = -1,
};
int tty_dev_add(const char *name, const struct tty_cfg *cfg)
{
    int ret;
    struct tty_device *tty = kmalloc(sizeof(struct tty_device));

    if (!tty)
        return -ENOMEM;

    memset(tty, 0, sizeof(struct tty_device));
    if (!cfg) cfg = &tty_cfg_def;
    memcpy(&tty->cfg, cfg, sizeof(struct tty_cfg));

    tty->stream_dev.ops = &tty_ops;

    ret = stream_device_register(&tty->stream_dev, name, NULL);
    if (ret < 0)
        kfree(tty);

    return ret;
}
void tty_dev_del(device_t *dev)
{
    struct stream_device *stream_dev = to_stream_dev(dev);
    struct tty_device *tty = to_tty(stream_dev);

    stream_device_unregister(stream_dev);
    kfree(tty);
}

